/**
 * 这里都是对云函数、云存储的操作。
 */
import tcb from "tcb-js-sdk";

import { getImgsTempUrlType } from "CustomTypes";
import request from "@/utils/requestLoginCloud";

import { successResponseType, failRejectType } from "CustomTypes";
import { reqMethod, isFailed } from "@/utils/http";
import { initSuccessResponseData, initFailRejectData } from "./http";
import { MessageBox } from "element-ui";
import { encodeDataByAesKey, getEncodedAesKey } from "@/utils/algorithm";
import { needAuth } from ".";
import { isObjectLike, isUndefined } from "lodash";
import { getAccessToken } from "./token";
import { LoadingModule } from "@/store/modules/LoadingModule";

type TCB = typeof tcb;

/**
 * 这个一般用来做“云接入”
 * return {TCB}
 */
export const getAppWithoutAuth = (): TCB => {
  const env = process.env.VUE_APP_CLOUD_ENV + "" || "";
  return tcb.init({ env });
};
/**
 * 是否已经登录云平台
 */
export const hasLoginedCloud = (): boolean => {
  const state = getAppWithoutAuth()
    .auth()
    .hasLoginState();
  return !!state;
};
/**
 * 这里是获取云函数的实例
 */
export const getApp = (): TCB => {
  const app = getAppWithoutAuth();
  app.auth();
  return app;
};
/**
 *获取已经上传的图片的临时下载地址
 *
 * @export
 * @param {Array<string>} urls
 * @returns {(Promise<getImgsTempUrlType> | any)}
 */
export const getImgsTempUrl = (
  urls: Array<string>
): Promise<getImgsTempUrlType> | any => {
  return new Promise(resolve => {
    const app = getApp();
    if (isUndefined(app)) return;
    let fileListFromImgs: Array<getImgsTempUrlType> = [];

    app
      .getTempFileURL({
        fileList: urls
      })
      .then((res: { fileList: any }) => {
        const { fileList } = res;

        if (fileList.length > 0) {
          fileListFromImgs = fileList.map(
            (
              el: { code?: any; tempFileURL?: any; fileID?: any },
              index: number
            ) => {
              const { tempFileURL, fileID } = el;

              if (el.code === "SUCCESS") {
                return {
                  uid: -index,
                  name: tempFileURL.split("/").pop(),
                  status: "done",
                  url: tempFileURL,
                  dbUrl: fileID
                };
              } else {
                return {
                  uid: -index,
                  name: fileID,
                  status: "done",
                  url: fileID
                };
              }
            }
          );

          resolve({ fileList: fileListFromImgs });
        }
      });
  });
};
/**
 * 1、使用axios登录一般的链接，在云平台中登录。
 * 2、以后再使用云平台的特殊方法（非axios）登录自己的个人账户，或者请求数据。
 * @return {Promise<void>}
 */
export const loginCloud = async (): Promise<
  successResponseType | failRejectType
> => {
  return new Promise((resolve, reject) => {
    // 如果还没有登录
    const app = getApp();
    // 打开云平台中权限验证才需要这一句代码
    const authHeader = tcb.auth().getAuthHeader();

    /**
     * 注意，这里的request不能使用request.ts中的。
     * 必须重新启动一个requestAuth.ts专门使用axios登录TCB的。
     */
    request({
      method: reqMethod.get,
      url: process.env.VUE_APP_YCZS_LOGIN + "",
      headers: {
        ...authHeader
      }
    })
      .then((res: any) => {
        const { ticket } = res.data;

        app
          .auth()
          .customAuthProvider()
          .signIn(ticket)
          .then(() => {
            resolve(initSuccessResponseData());
          })
          .catch(err => {
            reject(initFailRejectData("成功获取ticket之后登录云平台失败", err));
          });
      })
      .catch((err: any) => {
        console.log(err);
        reject(initFailRejectData("登录云平台失败---自定义信息"));
      });
  });
};
/**
 * 随机生成AES密钥，然后AES密钥加密数据，同时使用公钥加密AES私钥。
 * 那么客户端发送的数据总共为：AES加密后的数据，公钥加密后的AES密钥。
 * 这里默认带token
 */
const encodeDataNew = (data: any, flagToken = true) => {
  const tempData = data;
  if (needAuth() && isObjectLike(tempData)) {
    const key = getEncodedAesKey();
    const data = encodeDataByAesKey(tempData);

    return flagToken
      ? {
          key,
          data,
          flagEncoding: true,
          accessToken: getAccessToken()
        }
      : {
          key,
          data,
          flagEncoding: true
        };
  }
  return data;
};
/**
 * 将/manage/category/list转化为云开发能够识别的形势。
 * @param name
 * @param data
 */
const formatName = (name: string, data: any) => {
  /**
   * 如果是'/manage/category/list'，会变成
   * [ '', 'manage', 'category', 'list' ]
   */
  const list = name.split("/");
  const resultData = {
    name: list[1],
    data: {
      $url: name.replace(`/${list[1]}`, ""),
      other: data
    }
  };

  return {
    ...resultData
  };
};
/**
 * 持续登录云平台
 */
const loginCloudContinuely = async (): Promise<boolean> => {
  // 如果没有登录云平台
  if (!hasLoginedCloud()) {
    const { result } = await loginCloud();

    if (isFailed(result.status)) {
      await MessageBox.confirm("登录云平台失败，请告知技术人员。", "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      });

      return false;
    }
  }
  return true;
};
/**
 *
 * @param name 请求的url名字
 * @param data 需要发送的数据
 * @param flagToken true表示带上accessToken，false表示不带。默认是true。
 */
const dataOperations = (name: string, data = {}, flagToken = true) => {
  // 在这里，根据环境变量加密数据
  const tempData = encodeDataNew(data, flagToken);
  // 看加密之后的数据。
  // debugger;
  const dataObj = formatName(name, tempData);
  return dataObj;
};
/**
 * 调用云函数
 * @param dataObj 加密、格式化之后的数据
 */
const callCloudFunc = (dataObj: any): Promise<any> => {
  const app = getApp();
  return new Promise((resolve, reject) => {
    app
      .callFunction(dataObj)
      .then((res: any) => {
        resolve(res);
      })
      .catch((err: any) => {
        reject(err);
      });
  });
};
/**
 * 不需要token的api请求。
 *
 * @param name
 * @param data
 * @return {Promise<*|void>}
 */
export const ajaxTcbCloudWithoutToken = async (
  name: string,
  data = {}
): Promise<any> => {
  const flag = await loginCloudContinuely();
  if (!flag) return;
  const dataObj = dataOperations(name, data, false);
  const result = await callCloudFunc(dataObj);
  return result;
};
/**
 * 注意，如果长时间不登录，那么会出现错误
 * “Unhandled Rejection (Error): [tcb-js-sdk] 未登录CloudBase”
 * 解决方法是：
 * 运行 actions.js 中的方法：logout()。
 * 删除 userOption 中的user信息，让程序跳转到 login 页面。
 * 用于一般的api请求。
 *
 * @param name
 * @param data
 * @param flagShowLoading 注意，前面的data一定要赋值为{}。等于false的时候，不再触发页面的加载特效。
 * @return {Promise<*|void>}
 */
export const ajaxTcbCloud = async (
  name: string,
  data = {},
  flagShowLoading = true
): Promise<any> => {
  if (flagShowLoading) LoadingModule.StartRequesting();
  const flag = await loginCloudContinuely();
  if (!flag) return;
  const dataObj = dataOperations(name, data);
  const result = await callCloudFunc(dataObj);
  if (flagShowLoading) LoadingModule.StopRequesting();
  return result;
};
/**
 * 专门用于没有登录/注册成功之前的，检查用户名数据加密。
 *  注意，这里是没有token的。
 * @param name api名字
 * @param data 数据，必须是object
 * @param flagEncoding 是否加密，默认是false，不加密
 */
export const ajaxTcbCloudExtends = async (
  name: string,
  data = {},
  flagEncoding = false
): Promise<any> => {
  const flag = await loginCloudContinuely();
  if (!flag) return;

  // 在这里，根据环境变量加密数据
  const tempData = flagEncoding ? encodeDataNew(data) : data;
  const dataObj = formatName(name, tempData);

  const result = await callCloudFunc(dataObj);
  return result;
};
